package pl.edu.agh.nn.net;

import java.util.ArrayList;
import java.util.List;

import pl.edu.agh.nn.conf.InvalidNetworkConfigurationException;
import pl.edu.agh.nn.layer.ILayer;
import pl.edu.agh.nn.layer.InputLayer;
import pl.edu.agh.nn.neuron.AbstractNeuron;
import pl.edu.agh.nn.neuron.InputNeuron;

public abstract class AbstractNetWithLayers<ALayer extends ILayer> extends AbstractNetWithConfiguration {

	protected List<ALayer> innerLayers;
	protected InputLayer inputLayer;
	protected ALayer outputLayer;

	@Override
	public double[] compute(double[] inputValues) {

		int j = 0;
		for (AbstractNeuron<?> in : inputLayer.getNeurons()) {
			InputNeuron inn = (InputNeuron) in;
			inn.setOutput(inputValues[j++]);
		}

		double[] results = new double[outputLayer.getNeurons().size()];

		if (innerLayers != null) {
			for (ILayer layer : innerLayers) {
				for (AbstractNeuron<?> op : layer.getNeurons()) {
					op.computeOutput();
				}
			}
		}
		int i = 0;
		for (AbstractNeuron<?> op : outputLayer.getNeurons()) {
			op.computeOutput();
			results[i++] = op.getOutput();
		}
		return results;
	}

	@Override
	public void configure() throws InvalidNetworkConfigurationException {
		if (ioConfiguration != null) {
			ioConfiguration.configure();
		}
		if ((inputLayer == null) || (outputLayer == null)) {
			throw new InvalidNetworkConfigurationException();
		}
		inputLayer.configure();
		ILayer previousLayer = inputLayer;
		if (innerLayers != null) {
			for (ALayer lc : innerLayers) {
				lc.setPreviousLayer(previousLayer);
				lc.configure();
				previousLayer = lc;
			}
		} else {
			innerLayers = new ArrayList<ALayer>();
		}
		outputLayer.setPreviousLayer(previousLayer);
		outputLayer.configure();
	}

	public List<ALayer> getInnerLayers() {
		return innerLayers;
	}

	public InputLayer getInputLayer() {
		return inputLayer;
	}

	public ALayer getOutputLayer() {
		return outputLayer;
	}

	public void setInnerLayers(List<ALayer> innerLayers) {
		this.innerLayers = innerLayers;
	}

	public void setInputLayer(InputLayer inputLayer) {
		this.inputLayer = inputLayer;
	}

	public void setOutputLayer(ALayer outputLayer) {
		this.outputLayer = outputLayer;
	}
}
